package com.zhang.sdk.utils.log;

import android.content.Context;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import com.zhang.sdk.utils.app.DateUtil;
import java.io.File;
import java.util.Locale;

/**
 * Log日志输出工具类
 */
public class LogUtil {
    private static boolean isDebug = false;
    private static boolean SHOW_LINE_NUMBER_IN_LOG = isDebug;// 是否在log中显示行号
    private static final String TAG = "com.thinta.bap";
    //是否已初始化
    private static boolean initFlag = false;
    private static SaveLogStrategy saveLogStrategy;
    //整个日志目录
    public static String initPath = "";
    public static String OPT_LOG = "";
    public static String CRASH_LOG = "";

    public static void init(boolean debugFlag, Context context) {
        isDebug = debugFlag;
        SHOW_LINE_NUMBER_IN_LOG = debugFlag;
        initFlag = true;
        if (saveLogStrategy != null || !isDebug) {
            return;
        }
        final int MAX_BYTES = 1024 * 1024;
        String diskPath = Environment.getExternalStorageDirectory().getPath() + File.pathSeparator
                + context.getPackageName();
        File file = new File(diskPath);
        if (!file.exists()) {
            file.mkdirs();
        }
        initPath = file.getAbsolutePath();
        diskPath=  initPath+file.separator;
        OPT_LOG = initPath(diskPath,"操作日志");
        CRASH_LOG = initPath(diskPath,"崩溃日志");
        ErrorLogUtil.init(context,CRASH_LOG);
        //底层崩溃日志
        ExceptionHandleNew.getInstance().init(context, CRASH_LOG, "崩溃日志");

        HandlerThread ht = new HandlerThread("Logger." + OPT_LOG);
        ht.start();
        Handler handler = new SaveLogStrategy.WriteHandler(ht.getLooper(), OPT_LOG, MAX_BYTES);
        saveLogStrategy = new SaveLogStrategy(handler);

    }

    /**
     * 生成日志目录
     *
     * @return
     */
    private static String initPath(String path, String type) {
        //日志类型_APP包名_登录人账号_IMEI_创建日期_XXXX.log  创建日期格式：yyyyMMdd，日志类型：01：业务执行过程日志；02：执行错误日志；03：崩溃日志
        path += type + "_";
        path += DateUtil.get2YDTime();
        return path;
    }

    /**
     * 打印Log.w信息
     *
     * @param aPrintLogStr 打印信息字符串
     */
    public static void w(String aPrintLogStr) {
        LOG(TAG, aPrintLogStr, Log.WARN, false);
    }

    public static void w(String tag, String aPrintLogStr) {
        LOG(tag, aPrintLogStr, Log.WARN, false);
    }

    public static void i(String tag, String aPrintLogStr) {
        LOG(tag, aPrintLogStr, Log.INFO, false);
    }

    public static void i(String aPrintLogStr) {
        LOG(TAG, aPrintLogStr, Log.INFO, false);
    }

    public static void callerI(String aPrintLogStr) {
        LOG(TAG, aPrintLogStr, Log.INFO, true);
    }

    public static void d(String tag, String aPrintLogStr) {
        LOG(tag, aPrintLogStr, Log.DEBUG, false);
    }

    public static void d(String aPrintLogStr) {
        LOG(TAG, aPrintLogStr, Log.DEBUG, false);

    }

    public static void e(String tag, String aPrintLogStr) {
        LOG(tag, aPrintLogStr,  Log.ERROR, false);
    }

    public static void e(String aPrintLogStr) {
        LOG(TAG, aPrintLogStr,  Log.ERROR, false);
    }

    public static void v(String tag, String aPrintLogStr) {
        LOG(tag, aPrintLogStr, Log.VERBOSE, false);
    }

    /**
     * @param tag        log的tag
     * @param content    log的内容
     * @param logType    log的类型,如Log.INFO,Log.DEBUG等
     * @param showCaller 是否显示调用者的方法名和行号等
     */
    private static void LOG(String tag, String content, int logType, boolean showCaller) {
        if(!initFlag) {
            LogUtil.e("LogUtil","LogUtil没有初始化，请先初始化后使用，如果未初始化Log将不会输出显示");
        }
        if (isDebug) {
            if (SHOW_LINE_NUMBER_IN_LOG) {
                Throwable throwable = new Throwable();

                int methodDepth = 2;//LogUtils.LOGI的depth是0, 该函数的depth是1, 调用者的depth是2,调用者的调用者是3
                StackTraceElement[] stackTraceElements = throwable.getStackTrace();

                if (methodDepth < stackTraceElements.length) {
                    StackTraceElement element = stackTraceElements[methodDepth];

                    if (showCaller) {
                        if ((methodDepth + 1) < stackTraceElements.length) {
                            StackTraceElement elementCaller = stackTraceElements[methodDepth + 1];

                            if (elementCaller != null && elementCaller.getFileName() != null) {

                                String callerClassName = elementCaller.getFileName().substring(0, elementCaller.getFileName().lastIndexOf("."));
                                String className = element.getFileName().substring(0, element.getFileName().lastIndexOf("."));
                                String loginfo = String.format(Locale.getDefault(),"[%s.%s() (%s:%d)]", callerClassName, elementCaller.getMethodName(), elementCaller.getFileName(), elementCaller.getLineNumber());
                                loginfo = loginfo + "\n";
                                loginfo = loginfo + "    " + String.format(Locale.getDefault(),"[%s.%s() (%s:%d)]%s", className, element.getMethodName(), element.getFileName(), element.getLineNumber(), content);

                                content = loginfo;
                            }

                        }

                    } else {
                        if (element != null && element.getFileName() != null) {
                            String className = element.getFileName().substring(0, element.getFileName().lastIndexOf("."));
                            String loginfo = String.format(Locale.getDefault(),"[%s.%s() (%s:%d)]%s", className, element.getMethodName(), element.getFileName(), element.getLineNumber(), content);
                            content = loginfo;
                        }
                    }

                }
            }

            switch (logType) {
                case Log.INFO:
                    Log.i(tag, "===================>"+content);
                    break;
                case Log.DEBUG:
                    Log.d(tag, "===================>"+content);
                    break;
                case  Log.ERROR:
                    Log.e(tag, "===================>"+content);
                    break;
                case Log.WARN:
                    Log.w(tag, "===================>"+content);
                    break;
                case Log.VERBOSE:
                    Log.v(tag, "===================>"+content);
                    break;
                default:
                    break;
            }
            logPrint(logType, DateUtil.currentTimeFormat()+" "+content);
        }
    }

    private static void logPrint(int logLevel, Object msg) {
        if (isDebug) {
            String name = getFunctionName();
            if (saveLogStrategy != null) {
                saveLogStrategy.log(Log.ERROR, TAG, name + " - " + msg);
            }
            Log.println(logLevel, TAG, name + " - " + msg);
        }
    }

    /**
     * 获取当前方法名
     *
     * @return 方法名
     */
    private  static String getFunctionName() {
        StackTraceElement[] sts = Thread.currentThread().getStackTrace();
        if (sts == null) {
            return null;
        }

        for (StackTraceElement st : sts) {
            if (st.isNativeMethod()) {
                continue;
            }
            if (st.getClassName().equals(Thread.class.getName())) {
                continue;
            }
            if (st.getClassName().equals(LogUtil.class.getName())) {
                continue;
            }

            Thread t = Thread.currentThread();
            return "[Thread(id:" + t.getId() +
                    ", name:" + t.getName() +
                    ", priority:" + t.getPriority() +
                    ", groupName:" + t.getThreadGroup().getName() +
                    "): " + st.getFileName() + ":"
                    + st.getLineNumber() + " " + st.getMethodName() + " ]";
        }
        return "";
    }
}
